﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Com.FirstSolver.Security;
using System.Security;
//OpenSM.dll 已导入

namespace KeyperDev     
{
    public class cryptogram  
    {
        /// <summary>
        /// SM3杂凑算法
        /// 输入明文str:长度无限制，运行速度影响不大
        /// 密钥secretKey:长度无限制，运行速度影响不大
        /// 轮数:10<round<1000。
        /// 输出64位16进制hash值
        /// </summary>
        /// <param name="str"></param>
        /// <returns>密文str</returns>

        public static string mySM3(string str)   //SM3杂凑算法
        {
            string secretKey = "DEFAULT@keyper";
            int round = 100;
            byte[] plainMsg = Encoding.UTF8.GetBytes(str);
            Console.WriteLine("plainMsg: \n" + str);
            /*            if (String.IsNullOrEmpty(secretKey))//如果密钥为空,已注释
                        {
                            using (SM3 sm3 = new SM3())
                            {
                                for (int i = 0; i < round; i++)
                                {
                                    sm3.BlockUpdate(plainMsg);
                                }
                                byte[] Hash = sm3.DoFinal();
                                str = Utils.ToString(Hash);
                            }
                        }
                        else
                        {
            */
            using (HMACSM3 hmacsm3 = new HMACSM3(Encoding.UTF8.GetBytes(secretKey)))
            {
                for (int i = 0; i < round; i++)
                {
                    hmacsm3.BlockUpdate(plainMsg);
                }
                byte[] Hash = hmacsm3.DoFinal();
                str = Utils.ToString(Hash);
            }
            /*            }
            */
            Console.WriteLine("Result: \n" + str);
            return str;
        }

        /// <summary>
        /// SM4通过用户密码加密。主要用于生成密钥
        /// 根据加解密算法密钥长度必须为16bytes。
        /// 加密模式：PCBC风险：交换相邻密文段在解密时不会出错。CTR.CFB会因明文长短变化而变化容易猜测
        /// 填充模式：ISO10126以外的填充过于简单。（纯数字或字母时）明文每增加16位密文增加32位。
        /// </summary>
        /// <param name="str">明文消息</param>
        /// <param name="password">用户密码，为空时采用系统默认密码</param>
        /// <returns>密文</returns>
        public static string mySM4Encrpt(string str, string password) //SM4分组算法,使用用户密码加密（指纹密码、用户登录密码）
        {
            byte[] secretKey = Encoding.UTF8.GetBytes("");

            //用户密码如果不够16位则重复填充
            if (password.Equals(""))
            {
                secretKey = Encoding.UTF8.GetBytes("DefaultPw@keyper"); //必须是16个bytes
            }
            else if (password.Length >= 16)
            {
                secretKey = Encoding.UTF8.GetBytes(password.Substring(0, 16));
            }
            else if (password.Length < 16 && password.Length > 0)
            {
                String temp = "";
                do
                {
                    int sub = 16 - temp.Length;
                    if (sub > password.Length)
                    {
                        temp = temp + password;
                    }
                    else
                    {
                        temp = temp + password.Substring(0, sub);
                    }

                    if (temp.Length > 16)
                        return "用户密码填充出错\n";
                }
                while (temp.Length != 16);
                secretKey = Encoding.UTF8.GetBytes(temp);
            }


            byte[] IV = Encoding.UTF8.GetBytes("DefaultIV@keyper");

            Console.WriteLine("secretKey \n" + Encoding.UTF8.GetString(secretKey));
            Console.WriteLine("IV \n" + Utils.ToString(IV));

            CipherMode mode = CipherMode.PCBC;  //加密模式
            PaddingMode padding = PaddingMode.ISO10126;  //填充模式
        
            str = SM4ExecuteEncrypt(str, mode, padding, secretKey, IV);

            Console.WriteLine("encryptMsg \n" + str);

            //解密函数测试
            String de = SM4ExecuteDecrypt(str, mode, padding, secretKey, IV);
                      Console.WriteLine("decryptMsg \n" + de);

            return str;  //返回加密结果

        }
        public static string mySM4Decrpt(string str, string password) //SM4分组算法,使用用户密码解密（指纹密码、用户登录密码）
        {
            byte[] secretKey = Encoding.UTF8.GetBytes("");

            //用户密码如果不够16位则重复填充
            if (password.Equals(""))
            {
                secretKey = Encoding.UTF8.GetBytes("DefaultPw@keyper"); //必须是16个bytes
            }
            else if (password.Length >= 16)
            {
                secretKey = Encoding.UTF8.GetBytes(password.Substring(0, 16));
            }
            else if (password.Length < 16 && password.Length > 0)
            {
                String temp = "";
                do
                {
                    int sub = 16 - temp.Length;
                    if (sub > password.Length)
                    {
                        temp = temp + password;
                    }
                    else
                    {
                        temp = temp + password.Substring(0, sub);
                    }

                    if (temp.Length > 16)
                        return "用户密码填充出错\n";
                }
                while (temp.Length != 16);
                secretKey = Encoding.UTF8.GetBytes(temp);
            }


            byte[] IV = Encoding.UTF8.GetBytes("DefaultIV@keyper");

            Console.WriteLine("secretKey \n" + Encoding.UTF8.GetString(secretKey));
            Console.WriteLine("IV \n" + Utils.ToString(IV));

            CipherMode mode = CipherMode.PCBC;  //加密模式
            PaddingMode padding = PaddingMode.ISO10126;  //填充模式

           
            String de = SM4ExecuteDecrypt(str, mode, padding, secretKey, IV);


            return de;  //返回解密结果

        }

        /// <summary>
        /// SM4通过默认方式加密。
        /// </summary>
        /// <param name="str">明文</param>
        /// <returns></returns>
        public static string mySM4Encrpt(string str) //SM4分组算法,重载，使用程序内置密码，（有安全问题，内置密码的保存，写死 在程序里不安全）
        {
            str = mySM4Encrpt(str, "");
            return str;
        }
        public static string mySM4Decrpt(string str) //SM4分组算法,重载，使用程序内置密码，（有安全问题，内置密码的保存，写死 在程序里不安全）
        {
            str = mySM4Decrpt(str, "");
            return str;
        }

        /// <summary>
        /// SM4加密子函数
        /// </summary>
        /// <param name="plainMsg">明文，长度不限</param>
        /// <param name="mode">加密模式，PCBC</param>
        /// <param name="padding">填充模式，ISO10126</param>
        /// <param name="secretKey">密钥，由mySM4生成</param>
        /// <param name="IV">初始向量，在mySM4随机生成</param>
        /// <returns>密文 16进制String</returns>
        private static String SM4ExecuteEncrypt(String plainMsg, CipherMode mode, PaddingMode padding, byte[] secretKey, byte[] IV)
        {
            Console.WriteLine("SM4ExecuteEncrypt \n" + plainMsg);

            String str = "";    //返回加密结果
            byte[] Message = Encoding.UTF8.GetBytes(plainMsg);  //得到的明文是UTF8格式 提取Bytes
            List<byte> Answer = new List<byte>();

            // 加密
            using (SM4 sm4 = new SM4(CipherDirection.Encryption, mode, padding))
            {
                sm4.Initialize(secretKey, IV);                //真正的加密步骤？

                if (sm4.TransformFinalBlock(Message, 0, Message.Length, Answer))
                {
                    str = Utils.ToString(Answer.ToArray()); //加密后是16进制
                }
                else
                {
                    // 加密失败
                    str = "加密失败！！！";
                }
            }
            return str;
        }

        /// <summary>
        /// SM4解密子函数
        /// </summary>
        /// <param name="plainMsg">密文，长度为32的倍数，16进制String</param>
        /// <param name="mode">加密模式，PCBC</param>
        /// <param name="padding">填充模式，ISO10126</param>
        /// <param name="secretKey">密钥，由mySM4生成</param>
        /// <param name="IV">初始向量，在mySM4随机生成</param>
        /// <returns>明文 UTF8格式String</returns>
        private static String SM4ExecuteDecrypt(String encryptMsg, CipherMode mode, PaddingMode padding, byte[] secretKey, byte[] IV)
        {
            Console.WriteLine("SM4ExecuteDecrypt \n" + encryptMsg);
            String str = "";    //返回加密结果
            List<byte> Answer = new List<byte>();

            ///转码 16进制String转byte[]
            encryptMsg = encryptMsg.Replace(" ", "");
            if (encryptMsg.Length % 2 != 0)
            {
                encryptMsg += " ";
            }
            byte[] Message = new byte[encryptMsg.Length / 2];  //Message为转码后的密文用于解密
            for (int i = 0; i < Message.Length; i++)
            {
                Message[i] = Convert.ToByte(encryptMsg.Substring(i * 2, 2), 16);
            }

            // 解密
            using (SM4 sm4 = new SM4(CipherDirection.Decryption, mode, padding))
            {
                sm4.Initialize(secretKey, IV);

                if (sm4.TransformFinalBlock(Message, 0, Message.Length, Answer))
                {
                    str = Encoding.UTF8.GetString(Answer.ToArray());
                }
                else
                {
                    str = "解密失败！！！";
                }
            }
            return str;
        }



        public static string getRandomPassword(int charnum, int specialcharflag)//生成随机密码（字符：0-9 + a-z + A-Z + 特殊字符）    
        {                                                                       //charnum字符个数（>6）；specialcharflag表示是否允许有特殊字符 ~!@#$%^&*()_+ ,0表示没有，1表示有
            //可以用StringBuild类表示拼接字符串                                 //剔除了大写字母O 和数字0
            StringBuilder RandPass = new StringBuilder();
            string str = "随机密码序列";
            long tick = DateTime.Now.Ticks;
            Random ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));
            while (true)
            {
                for (int i = 0; i < charnum; i++)
                {
                    int RandType = 0;
                    if (specialcharflag == 0)
                    {
                        RandType = ran.Next(0, 3);
                    }
                    else
                    {
                        RandType = 3;
                    }
                    switch (RandType)
                    {
                        case 0://数字
                            {
                                int Randcha = ran.Next(48, 57);
                                if ((char)Randcha == '0') 
                                {
                                    i--;
                                    break;
                                }
                                RandPass.Append((char)Randcha);
                                Console.Out.WriteLine(Randcha + ":" + (char)Randcha);
                                break;
                            }
                        case 1: //小写字母
                            {
                                int Randcha = ran.Next(97, 122);
                                RandPass.Append((char)Randcha);
                                break;
                            }
                        case 2: //大写字母
                            {
                                int Randcha = ran.Next(65, 90); 
                                if ((char)Randcha == 'O')
                                {
                                    i--;
                                    break;
                                }
                                RandPass.Append((char)Randcha);
                                break;
                            }
                        case 3: //特殊字符
                            {
                                int Randcha = ran.Next(33, 126);
                                RandPass.Append((char)Randcha);
                                break;
                            }
                    }
                }
                str = RandPass.ToString();
                if (randomnessTestValue(str) > 60)
                    return str;
                else
                {
                    RandPass.Clear();
                    continue;
                }

            }

        }

        public static int randomnessTestValue(string password) //检测随机性，符合标准返回0，否则返回-1
        {
            int pass_Length = password.Length;//密码长度
            int pass_flag_num = 0;
            if (pass_Length > 5)
                pass_flag_num = 1;
            int pass_score = 0;//最终得分
            int[] pass_flag = new int[4];//符合类型 数字 小写字母 大写字母 特殊符号
            int[] charc = new int[128];
            int pass_num = 0;//密码中数字的个数
            int pass_low = 0;//密码中小写字母的个数
            int pass_upp = 0;//密码中大写字母的个数
            int pass_sym = 0;//密码中特殊符号的个数
            int i = 0;
            int pass_mid_numsym = 0;//密码中中间符号的个数。
            //int pass_repeat = 0;
            //不连续的都是数字/小写字母/大写字母
            int pass_con_num = 0;
            int pass_con_low = 0;
            int pass_con_upp = 0;
            //都是连续的数字/字母/符号
            int pass_seq_num = 0;
            int pass_seq_let = 0;
            int pass_seq_sym = 0;

            for (i = 0; i < pass_Length; i++)
            {
                char c = password[i];
                //数字
                if (48 <= c && c <= 57)
                {
                    if (pass_flag[0] == 0)
                    {
                        pass_flag[0] = 1;
                        pass_flag_num = pass_flag_num + 1;
                    }
                    charc[c] = charc[c] + 1;
                    pass_num = pass_num + 1;
                    if (i > 0 && i < pass_Length - 1)
                        pass_mid_numsym = pass_mid_numsym + 1;
                    //3个连续数字
                    if (i > 1)
                    {
                        if (48 <= password[i - 1] && password[i - 1] <= 57)
                        {
                            if (48 <= password[i - 2] && password[i - 2] <= 57)
                            {
                                if (password[i - 1] == c + 1)
                                {
                                    if (password[i - 2] == c + 2)
                                    {
                                        pass_seq_num = pass_seq_num + 1;
                                    }
                                }
                                else if (password[i - 1] == c - 1)
                                {
                                    if (password[i - 2] == c - 2)
                                    {
                                        pass_seq_num = pass_seq_num + 1;
                                    }
                                }
                            }
                        }
                    }

                    //连续的数字
                    if (i < pass_Length - 1)
                    {
                        if (48 > password[i + 1] || password[i + 1] > 57)
                        {
                            for (int j = i - 1; j >= 0; j--)
                            {
                                if (48 <= password[j] && password[j] <= 57)
                                {
                                    pass_con_num = pass_con_num + 1;
                                }
                                else
                                    break;
                            }
                        }
                    }
                    else
                    {
                        for (int j = i - 1; j >= 0; j--)
                        {
                            if (48 <= password[j] && password[j] <= 57)
                            {
                                pass_con_num = pass_con_num + 1;
                            }
                            else
                                break;
                        }
                    }
                }
                //小写
                else if (97 <= c && c <= 122)
                {
                    if (pass_flag[1] == 0)
                    {
                        pass_flag[1] = 1;
                        pass_flag_num = pass_flag_num + 1;
                    }
                    charc[c] = charc[c] + 1;
                    pass_low = pass_low + 1;

                    //3个连续数字
                    if (i > 1)
                    {
                        if (97 <= password[i - 1] && password[i - 1] <= 122)
                        {
                            if (97 <= password[i - 2] && password[i - 2] <= 122)
                            {
                                if (password[i - 1] == c + 1)
                                {
                                    if (password[i - 2] == c + 2)
                                    {
                                        pass_seq_let = pass_seq_let + 1;
                                    }
                                }
                                else if (password[i - 1] == c - 1)
                                {
                                    if (password[i - 2] == c - 2)
                                    {
                                        pass_seq_let = pass_seq_let + 1;
                                    }
                                }
                            }
                        }
                    }

                    //连续的小写字母
                    if (i < pass_Length - 1)
                    {
                        if (97 > password[i + 1] || password[i + 1] > 122)
                        {
                            for (int j = i - 1; j >= 0; j--)
                            {
                                if (97 <= password[j] && password[j] <= 122)
                                {
                                    pass_con_low = pass_con_low + 1;
                                }
                                else
                                    break;
                            }
                        }
                    }
                    else
                    {
                        for (int j = i - 1; j >= 0; j--)
                        {
                            if (97 <= password[j] && password[j] <= 122)
                            {
                                pass_con_low = pass_con_low + 1;
                            }
                            else
                                break;
                        }
                    }
                }
                //大写
                else if (65 <= c && c <= 90)
                {
                    if (pass_flag[2] == 0)
                    {
                        pass_flag[2] = 1;
                        pass_flag_num = pass_flag_num + 1;
                    }
                    pass_upp = pass_upp + 1;
                    charc[c] = charc[c] + 1;

                    //3个连续大写字母
                    if (i > 1)
                    {
                        if (65 <= password[i - 1] && password[i - 1] <= 90)
                        {
                            if (65 <= password[i - 2] && password[i - 2] <= 90)
                            {
                                if (password[i - 1] == c + 1)
                                {
                                    if (password[i - 2] == c + 2)
                                    {
                                        pass_seq_let = pass_seq_let + 1;
                                    }
                                }
                                else if (password[i - 1] == c - 1)
                                {
                                    if (password[i - 2] == c - 2)
                                    {
                                        pass_seq_let = pass_seq_let + 1;
                                    }
                                }
                            }
                        }
                    }

                    //连续的大写字母
                    if (i < pass_Length - 1)
                    {
                        if (65 > password[i + 1] || password[i + 1] > 90)
                        {
                            for (int j = i - 1; j >= 0; j--)
                            {
                                if (65 <= password[j] && password[j] <= 90)
                                {
                                    pass_con_upp = pass_con_upp + 1;
                                }
                                else
                                    break;
                            }
                        }
                    }
                    else
                    {
                        for (int j = i - 1; j >= 0; j--)
                        {
                            if (65 <= password[j] && password[j] <= 90)
                            {
                                pass_con_upp = pass_con_upp + 1;
                            }
                            else
                                break;
                        }
                    }

                }
                else
                {
                    if (pass_flag[3] == 0)
                    {
                        pass_flag[3] = 1;
                        pass_flag_num = pass_flag_num + 1;
                    }
                    pass_sym = pass_sym + 1;
                    charc[c] = charc[c] + 1;
                    if (i > 0 && i < pass_Length - 1)
                        pass_mid_numsym = pass_mid_numsym + 1;
                }



            }
            //add
            pass_score = 0;
            pass_score += (password.Length * 4);
            //flag1=小写 2=大写 3=符号
            if (pass_flag[1] == 1 && (pass_flag[0] == 1 || pass_flag[2] == 1 || pass_flag[3] == 1))
            {
                pass_score = pass_score + ((pass_Length - pass_low) * 2);
            }
            if (pass_flag[2] == 1 && (pass_flag[0] == 1 || pass_flag[1] == 1 || pass_flag[3] == 1))
            {
                pass_score = pass_score + ((pass_Length - pass_upp) * 2);
            }
            if (pass_flag[0] == 1 && (pass_flag[1] == 1 || pass_flag[2] == 1 || pass_flag[3] == 1))
            {
                pass_score = pass_score + pass_num * 4;
            }

            pass_score = pass_score + pass_sym * 6;
            if (pass_flag_num >= 4)
            {
                pass_score = pass_score + pass_flag_num * 2;
            }
            pass_score = pass_score + pass_mid_numsym * 2;

            //deductions
            if (pass_flag[0] == 1 && pass_flag[1] == 0 && pass_flag[2] == 0 && pass_flag[3] == 0)
            {
                pass_score = pass_score - pass_num;
            }
            if (pass_flag[1] == 1 && pass_flag[0] == 0 && pass_flag[2] == 0 && pass_flag[3] == 0)
            {
                pass_score = pass_score - pass_upp - pass_low;
            }
            pass_score = pass_score - pass_con_num ;
            pass_score = pass_score - pass_con_upp ;
            pass_score = pass_score - pass_con_low ;

            pass_score = pass_score - pass_seq_let * 2;
            pass_score = pass_score - pass_seq_num * 2;
            pass_score = pass_score - pass_seq_sym * 2;
            if (pass_score > 100)
                pass_score = 100;
            if (pass_score < 0)
                pass_score = 0;
            return pass_score;
        }

        
    }
}
